/** @file matrix_dataset.h
 *
 *  @author Dongryeol Lee (drselee@gmail.com)
 */

#ifndef CORE_DATA_MATRIX_DATASET_H
#define CORE_DATA_MATRIX_DATASET_H

#include <armadillo>
#include <boost/serialization/serialization.hpp>
#include <fstream>
#include <vector>

#include "core/data/dataset.h"
#include "core/data/alias_vec.h"

namespace boost {
namespace serialization {

template <class Archive>
void save(Archive & ar, const arma::mat & c, const unsigned int version)   {
  ar & c.n_rows;
  ar & c.n_cols;
  for(int j = 0; j < static_cast<int>(c.n_cols); j++) {
    for(int i = 0; i < static_cast<int>(c.n_rows); i++) {
      double elem = c.at(i, j);
      ar & elem;
    }
  }
}

template <class Archive>
void load(Archive & ar, arma::mat & c, const unsigned int version)  {
  int rows, cols;
  ar & rows;
  ar & cols;
  c.zeros(rows, cols);
  for(int j = 0; j < static_cast<int>(c.n_cols); j++) {
    for(int i = 0; i < static_cast<int>(c.n_rows); i++) {
      ar & c.at(i, j);
    }
  }
}

template <class Archive>
void serialize(Archive & ar, arma::mat & c, const unsigned int version)   {
  boost::serialization::split_free(ar, c, version);
}

template<class Archive>
void serialize(
  Archive & ar,
  std::pair <
  std::pair<int, int>, std::pair<int, int> > & g,
  const unsigned int version) {
  ar & g.first.first;
  ar & g.first.second;
  ar & g.second.first;
  ar & g.second.second;
}
} // namespace serialization
} // namespace boost

namespace core {
namespace data {

class DatasetParserTest;

class MatrixDataset : public core::data::Dataset {
  private:
    friend class core::data::DatasetParserTest;

  private:

    std::vector< arma::vec * > col_collections_;

    arma::mat matrix_;

    /** @brief The view consisting of none-labels.
     */
    std::vector < core::data::alias_vec * > raw_point_collections_;

    /** @brief The view consisting of the targets.
     */
    std::vector < core::data::alias_vec * > raw_target_collections_;

  public:

    MatrixDataset();

    ~MatrixDataset();

    double average_num_points() const;

    arma::vec &get_instance(int point_id);

    const arma::vec &get_instance(int point_id) const;

    core::data::alias_vec &get_instance(
      int point_id, core::data::alias_vec **targets_out);

    const core::data::alias_vec &get_instance(
      int point_id, const core::data::alias_vec **targets_out) const;

    void Init(
      boost::mpi::communicator &world, int num_attributes, int num_points);

    void Init(int num_attributes, int num_points);

    bool Init(const std::string &file_name_in);

    void Init(
      boost::mpi::communicator &world,
      const std::string &file_name_in, bool from_single_file) ;

    int num_points() const;

    void RandomGenerate(
      boost::mpi::communicator &world, int num_dimensions, int num_points);
};
}
}

#endif
